<template>
  <div class="rank-box">
    <div :class="['header', 'margin-bottom20']">
      <el-button class="refresh" size="default" type="primary" @click="excel"
        >导出</el-button
      >
      <el-button class="refresh" size="default" type="primary" @click="start"
        >刷新</el-button
      >
      <!-- 筛选成员 可编辑、可多选的下拉框 -->
      <el-select
        class="margin-left20"
        style="width: 200px"
        v-model="selectedUsers"
        size="default"
        filterable
        :filter-method="filterMethod"
        :clearable="true"
        multiple
        collapse-tags
        collapse-tags-tooltip
        placeholder="可输入成员名称查询"
      >
        <el-option
          v-for="item in selectUsers"
          :key="item.ID"
          :value="item.ID!"
          :label="item.xingming ? `${item.xingming} (${item.name})` : item.name"
        >
          {{ item.xingming ? `${item.xingming} (${item.name})` : item.name }}
        </el-option>
      </el-select>
      <el-text size="default" type="info" class="margin-left20">
        <el-icon>
          <InfoFilled />
        </el-icon>
        比赛结束后7日内补题有效（只能补编程题）。该界面得分显示7日内各题目补题最高分。
      </el-text>
    </div>

    <!-- 表格 -->
    <el-table
      :size="buttonSize"
      v-loading="loading"
      class="match-rank-table"
      :data="tableData"
      style="width: 100%"
      :span-method="arraySpanMethod"
    >
      <template v-slot:empty>
        <el-empty
          image="https://cdn-hterobot.oss-cn-qingdao.aliyuncs.com/image/ojSass-empty.png"
        />
      </template>

      <el-table-column
        type="index"
        label="排名"
        align="center"
        :width="variant === 'mathH5' ? '60' : '50'"
        :fixed="variant === 'mathH5'"
      ></el-table-column>

      <el-table-column
        prop="name"
        label="成员"
        align="center"
        :width="variant === 'mathH5' ? '160' : '120'"
        :fixed="variant === 'mathH5'"
        show-overflow-tooltip
      >
        <template #default="{ row }">
          {{ row.xingming ? `${row.xingming} (${row.name})` : row.name }}
        </template>
      </el-table-column>

      <!-- 总分列组 -->
      <el-table-column prop="" label="" align="center" width="">
        <template #header>
          <div class="icon-text">
            <span>总分</span>
            <el-tooltip effect="dark" content="" placement="top">
              <img
                src="https://cdn-hterobot.oss-cn-qingdao.aliyuncs.com/JOY/LOGO/wenhao.png"
                alt=""
                style="width: 16px; margin-left: 2px"
              />
              <template #content>
                <div>比赛成绩：比赛各题的每道题得分之和</div>
                <div>
                  补题成绩：赛后补题的每道题得分之和（比赛满分题目记为满分，非满分题目以补题得分为准）
                </div>
                <div>
                  汇总成绩：对每道题取"比赛得分及补题得分"的最大值，汇总每道题得分
                </div>
              </template>
            </el-tooltip>
          </div>
        </template>
        <template #default>
          <el-table-column
            prop="matchTotalScore"
            :label="variant === 'mathH5' ? '比赛成绩' : '比赛'"
            align="center"
            fixed
            sortable
            :width="variant === 'mathH5' ? '110' : '76'"
          >
            <template #default="{ row }">
              <span
                :class="totalScoreClassName(row, 'matchTotalScore')"
                class="score"
              >
                {{ row.isAnswered ? row.matchTotalScore : "-" }}
              </span>
            </template>
          </el-table-column>
          <el-table-column
            prop="totalScore"
            :label="variant === 'mathH5' ? '补题成绩' : '补题'"
            align="center"
            fixed
            sortable
            :width="variant === 'mathH5' ? '110' : '76'"
          >
            <template #default="{ row }">
              <el-text
                :class="totalScoreClassName(row, 'totalScore')"
                class="score"
                type=""
              >
                {{ row.isPractise ? row.totalScore : "未补题" }}
              </el-text>
            </template>
          </el-table-column>
          <el-table-column
            prop="maxTotalScore"
            :label="variant === 'mathH5' ? '汇总成绩' : '汇总'"
            align="center"
            fixed
            sortable
            :width="variant === 'mathH5' ? '110' : '76'"
          >
            <template #default="{ row }">
              <el-text
                :class="totalScoreClassName(row, 'maxTotalScore')"
                class="score"
                type=""
              >
                {{ row.maxTotalScore }}
              </el-text>
            </template>
          </el-table-column>
        </template>
      </el-table-column>

      <!-- 题目列 -->
      <el-table-column
        v-for="(item, key) in matchQuestion"
        :key="key"
        align="center"
        :prop="String(key)"
        :label="`#${key + 1}(${item.score})`"
        class-name="el-table-column"
        label-class-name="match-rank-table-header"
        width=""
      >
        <template #header="">
          <el-tooltip
            effect="dark"
            :content="`${item.type}-${item.name}`"
            placement="top"
          >
            <div>
              <div class="match-rank-table-header-qNo">
                {{ `# ${key + 1} ` }}
              </div>
              <div class="match-rank-table-header-qScore">
                {{ `${item.name} ` }}
              </div>
            </div>
          </el-tooltip>
        </template>

        <el-table-column :prop="String(key)" label="比赛" align="center">
          <template #default="{ row }">
            <span :class="spanClassName(row.rank, key)" class="score">
              {{ row.isAnswered ? row.rank[key].matchScore : "-" }}
            </span>
          </template>
        </el-table-column>

        <el-table-column :prop="String(key)" label="补题" align="center">
          <template #default="{ row }">
            <div v-if="row.rank[key]?.isPractise">
              <el-tooltip
                v-if="iswatchcode || isAdmin || row.userId === user?.ID"
                effect="dark"
                :content="`点击查看代码详情`"
                placement="top"
              >
                <div
                  :class="className(row.rank, key)"
                  class="cursor-pointer score-box"
                  @click="showCode(row, key)"
                >
                  {{ row.rank[key].df || 0 }}
                </div>
              </el-tooltip>
              <div v-else :class="className(row.rank, key)" class="score-box">
                {{ row.rank[key].df || 0 }}
              </div>
            </div>
            <div v-else>
              <div v-if="user?.ID === row.ID">
                <el-button type="primary" @click="toQuestion(row, key)"
                  >去补题</el-button
                >
              </div>
              <div v-else>未补题</div>
            </div>
          </template>
        </el-table-column>
      </el-table-column>
    </el-table>

    <!-- 弹窗 -->
    <el-dialog
      class="dialog"
      v-model="dialogVisible"
      width="96%"
      :before-close="handleClose"
    >
      <template #header>
        <span class="icon-text el-dialog__header-title">
          <el-icon class="text-primary">
            <Guide />
          </el-icon>
          {{ sourceDetail.userName }} - {{ sourceDetail.name }} -
          {{ sourceDetail.score }}
        </span>
      </template>
      <div class="">
        <h6 class="source-detail-title margin-bottom0">
          提交时间:{{ TimeYH(sourceDetail.createdAt) }}
          <el-tooltip
            v-if="sourceDetail.userid === user.ID"
            class="box-item"
            effect="dark"
            content="更多我的提交"
            placement="right-end"
          >
            <el-button @click="goToSubmission(sourceDetail)">
              查看更多
            </el-button>
          </el-tooltip>
          <el-tooltip
            v-else-if="hasPermission"
            class="box-item"
            effect="dark"
            content="成员提交记录"
            placement="right-end"
          >
            <div v-if="variant === 'acCoderSaas' || variant === 'ojSass'">
              <el-button type="primary" @click="goToAssessment(sourceDetail)">
                补题提交记录
              </el-button>
            </div>
          </el-tooltip>
        </h6>
      </div>
      <el-row class="dialog-content">
        <el-col :span="8" class="left">
          <el-row class="test-case-title">
            <h6 class="margin-bottom0">测试点信息</h6>
          </el-row>
          <el-scrollbar>
            <div
              v-if="Array.isArray(sourceDetail.info)"
              v-for="(info1, infok) in subtask(sourceDetail.info)"
              :key="'a_' + infok"
            >
              <div class="col-12">Subtask {{ infok + 1 }}</div>
              <div class="col-12">
                <div
                  class="test-case-container"
                  v-for="(e, k) in info1"
                  :key="`b_${k}`"
                  :class="e.tn ? e.tn[1] : ALLTYPE['RE'][1]"
                >
                  <div class="test-case-content">
                    <div class="test-case-head"></div>
                    <strong class="test-case-score">{{ e.score || 0 }}</strong>
                    <span class="test-case-time font-size13"> </span>
                  </div>
                </div>
              </div>
            </div>
            <el-row v-else>
              <div class="col p-0 mb-4 mt-4">
                <h5>错误提示</h5>
                <pre class="m-0 code-pre" v-text="sourceDetail.info?.msg"></pre>
              </div>
            </el-row>
          </el-scrollbar>
        </el-col>
        <el-col :span="16" class="right">
          <el-row class="source-code-title">
            <h6 class="margin-bottom0">源代码</h6>
            <div class="margin-left10">
              <el-icon
                class="icon cursor-pointer"
                @click="myCopy(sourceDetail.sourceCode || '')"
              >
                <CopyDocument />
              </el-icon>
            </div>
          </el-row>
          <div class="col p-0">
            <pre v-highlight :class="'language-' + sourceDetail.sourceLanguage">
                    <code v-text="sourceDetail.sourceCode" class="code-pre"></code>
                </pre>
          </div>
        </el-col>
      </el-row>
      <template #footer>
        <el-button type="primary" size="small" @click="dialogVisible = false"
          >关闭</el-button
        >
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { useClipboard } from "@vueuse/core";
import { ElMessage, TableColumnCtx } from "element-plus";
import { InfoFilled, Guide, CopyDocument } from "@element-plus/icons-vue";
import { computed, onMounted, reactive, ref, watch } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import {
  asyncgetactivity,
  asyncgetteam,
  asyncselectmatchquestion,
  asyncSelectmatchsummary,
  asyncselectuserquestion,
  asyncteamselectmatchuserquestion,
  Iobj,
  selectteamrole,
} from "../api";
import { Storekey } from "../interface";
import Studycodemodel from "../mysql_interface/studycodemodel";
import { getExcelMerges } from "../util/excel";
import { ALLTYPE } from "@/util/jianchatime";
import subtask from "../util/subtask";
import { TimeYH } from "../util/time";

interface TableData {
  ID?: number | undefined;
  name?: string | undefined;
  xingming?: string | undefined;
  headportrait?: string | undefined;
  isPractise?: boolean | undefined;
  totalScore?: number | undefined;
  isAnswered?: boolean | undefined;
  matchTotalScore?: number | undefined;
  maxTotalScore?: number | undefined;
  rank?: Rank[];
}

interface Rank {
  userQuestionId: number | undefined;
  questionName: string | undefined;
  questionId: number | undefined;
  type: Studycodemodel.Iquestiontype | undefined;
  score: number;
  isPractise: boolean;
  df: number | undefined;
  resultdata: string;
  createdAt: Date | undefined;
  matchQScore: number | undefined;
  isQuestionAnswered: boolean;
  matchScore: number;
  matchResultdata: string;
}

type VariantType = "acCoderH5" | "acCoderSaas" | "mathH5" | "ojSass";

interface Props {
  activity: Studycodemodel.Iactivity;
  variant?: VariantType;
}

const props = withDefaults(defineProps<Props>(), {
  variant: "acCoderH5",
});

const { copy } = useClipboard();
const router = useRouter();
const store = useStore(Storekey);

const user = computed(() => {
  return store.state.user;
});

const teamId = computed(() => {
  return user.value?.teamid;
});

const isAdmin = computed(() => {
  return user.value?.isAdmin || user.value?.remarks == "销售助教";
});

// 根据variant确定按钮大小
const buttonSize = computed(() => {
  return props.variant === "mathH5" ? "small" : "default";
});

const loading = ref(true);
const limit = ref(9999);
const offset = ref(0);
const activity = reactive<Studycodemodel.Iactivity>({});

const format = computed(() => {
  return props.activity.format;
});

const iswatchcode = computed(() => {
  return activity.iswatchcode;
});

watch(
  () => props.activity.ID,
  (v) => {
    if (v) start();
  }
);

onMounted(async () => {
  await start();
  getPermissionsCfg();
});

// 获取权限
const roleName = "学生";
const routerNames = ref<any[]>([]);

async function getPermissionsCfg() {
  const result = await selectteamrole({ teamid: teamId.value, name: roleName });
  const { rows } = result;
  routerNames.value = rows[0]?.routername || [];
}

const hasPermission = computed(() => {
  return isAdmin.value || routerNames.value.find((e) => e === "评测");
});

async function start() {
  if (!props.activity.ID) return;
  loading.value = true;

  // 同步比赛信息
  const data = await asyncgetactivity({ ID: props.activity.ID });
  Object.assign(activity, data);

  // 不展示排行榜逻辑修改：赛后成绩、补题成绩都是展示TAB但是不显示数据
  if (
    !activity.isranking &&
    (props.variant === "acCoderSaas" || props.variant === "ojSass")
  ) {
    loading.value = false;
    return;
  }

  await init();
  loading.value = false;
}

// 报名人员
const users = ref<Studycodemodel.Iuser[]>([]);
// 比赛题目
const matchQuestion = ref<Studycodemodel.Imatchquestion[]>([{ score: 0 }]);
// 赛后比赛题目练习记录
const userQuestion = ref<Studycodemodel.Iuserquestion[]>([]);
// 比赛成绩
const matchRanking = ref<Studycodemodel.Imatchranking[]>([]);

async function getMatchRanking() {
  // 获取团队成员、管理员显示比赛记录
  const teamInfo = await asyncgetteam({ ID: activity.teamid });
  const isadminshowactivity = teamInfo?.isadminshowactivity;
  // 团队所有管理
  const teamAdminUsers = teamInfo?.teamuser || [];

  // 1. 获取数据
  const { rows } = await asyncteamselectmatchuserquestion({
    activityid: props.activity.ID,
    teamid: activity.teamid || teamId.value,
    _offset: offset.value,
    _limit: limit.value,
  });

  // 2. 预处理题目数据
  const questions = matchQuestion.value;
  const questionIndexMap = new Map(questions.map((q, index) => [q.ID, index]));

  // 3. 处理用户数据
  const result = new Map();
  for (const row of rows) {
    const {
      userid,
      user,
      questionid,
      df = 0,
      type = "代码",
      resultdata = "UN",
      info,
    } = row;
    // 3.1 跳过不在题目列表的 questionid
    const index = questionIndexMap.get(questionid);
    if (index === undefined) continue;
    // 3.2 初始化用户数据
    if (!result.has(userid)) {
      result.set(userid, {
        user: {
          ID: userid,
          name: user?.name || "",
          xingming: user?.xingming || "",
        },
        totalScore: 0,
        firstRow: row,
        questionData: new Array(questions.length).fill(null),
      });
    }
    // 3.3 更新用户分数 & 题目信息
    const userData = result.get(userid);
    userData.totalScore += df;
    userData.questionData[index] = [
      type,
      df,
      questionid,
      resultdata,
      Array.isArray(info)
        ? info.map(({ score = 0, t = 0, memory = 0, tn = [] }) => [
            score,
            t,
            memory,
            tn[0] || "UN",
          ])
        : [],
    ];
  }

  // 4. 生成最终排名数据
  matchRanking.value = Array.from(result.values()).map((userData) => {
    const filledData = userData.questionData.map(
      (data: any, i: number) => data || ["代码", 0, questions[i].ID, "UN", []]
    );
    return {
      userid: userData.user.ID,
      activityid: userData.firstRow?.activityid,
      df: userData.totalScore,
      username: userData.user.name,
      starttime: userData.firstRow?.createdAt,
      endtime: userData.firstRow?.createdAt,
      createdAt: userData.firstRow?.createdAt,
      updatedAt: userData.firstRow?.updatedAt,
      data: JSON.stringify(filledData),
    };
  });

  // 5. 更新用户列表 增加判断：管理员是否显示成绩
  users.value = Array.from(result.values()).reduce((acc: any[], u: any) => {
    if (
      isadminshowactivity ||
      !teamAdminUsers.some((admin: any) => admin.userid === u.user.ID)
    ) {
      acc.push(u.user);
    }
    return acc;
  }, []);
}

async function init() {
  // 比赛题目
  const [data] = await asyncselectmatchquestion({
    activityid: activity.ID || 0,
  });
  matchQuestion.value = (data.matchquestion || []).filter((e) => {
    return e.type === "代码";
  });

  let cond: Iobj<Studycodemodel.Imatchsummary> = {
    activityid: activity.ID || 0,
    _offset: offset.value,
    _limit: limit.value,
  };

  const result = await asyncSelectmatchsummary(cond);
  // 注意：这里需要根据实际API返回结构调整
  userQuestion.value = (result as any).userquestion || [];
  users.value = (result as any).users || [];

  //
  if (!activity.isranking) return;
  await getMatchRanking();
}

const selectUsers = ref<Studycodemodel.Iuser[]>([]);
const copySelectUsers = ref<Studycodemodel.Iuser[]>([]);
const selectedUsers = ref<number[]>([]);

// 组装数据
const tableData = computed(() => {
  const ret = users.value.reduce((acc, u) => {
    const { ID } = u;

    // 查找比赛成绩和补题记录
    const matchranking = matchRanking.value.find((e) => e.userid === ID) || {};
    const userQuestions = userQuestion.value.filter((uq) => uq.userid === ID);

    if (!matchranking?.data && !userQuestions.length) {
      return acc;
    }

    const _data = JSON.parse(matchranking?.data || "[]");
    let totalScore = 0;
    let isPractise = false;
    let isAnswered = false;
    const matchTotalScore = matchranking?.df || 0;
    let maxTotalScore = 0;

    const rank: Rank[] = matchQuestion.value.map((e, i) => {
      const matchAnswered = _data[i]; // 现在数据顺序已经一致
      const matchScore = matchAnswered ? matchAnswered[1] || 0 : 0;
      const isQuestionAnswered = !!matchAnswered;
      isAnswered = isAnswered || isQuestionAnswered;
      const matchllAC =
        isQuestionAnswered && matchAnswered[3] === "AC" ? "AC" : "UN";

      // 查找补题记录
      const uq = userQuestions.find((uq) => uq.questionid === e.oldid);
      const makeupScore = uq?.df || 0;
      totalScore += makeupScore;
      isPractise = isPractise || !!uq;

      maxTotalScore += Math.max(makeupScore, matchScore);

      return {
        userQuestionId: uq?.ID,
        questionName: e.name,
        questionId: e.oldid,
        type: e.type,
        score: e.score || 100,
        isPractise: !!uq,
        df: makeupScore,
        resultdata: uq?.resultdataarr?.every((r) => r === "AC") ? "AC" : "UN",
        createdAt: uq?.createdAt,
        matchQScore: e.score,
        isQuestionAnswered,
        matchScore,
        matchResultdata: matchllAC,
      };
    });

    acc.push({
      rank,
      isPractise,
      totalScore,
      isAnswered,
      matchTotalScore,
      maxTotalScore,
      ...u,
    });
    return acc;
  }, [] as TableData[]);

  selectUsers.value = [...ret];
  copySelectUsers.value = [...ret];

  if (selectedUsers.value.length) {
    return ret.filter((e) => selectedUsers.value.includes(e.ID!));
  }
  return ret.sort((a, b) => {
    const dfDiff = Number(b.maxTotalScore) - Number(a.maxTotalScore);
    if (dfDiff !== 0) {
      return dfDiff;
    }
    return (b.xingming || "").localeCompare(a.xingming || "", "zh") || 0;
  });
});

interface SpanMethodProps {
  row: TableData;
  column: TableColumnCtx<TableData>;
  rowIndex: number;
  columnIndex: number;
}

const arraySpanMethod = ({
  row,
  column,
  rowIndex,
  columnIndex,
}: SpanMethodProps) => {
  const { rank, isAnswered, isPractise } = row;
  // 有比赛成绩
  if (isAnswered) {
    // 处理逻辑省略，与原文件相同
  }
  return [1, 1];
};

// 输入框过滤方法
function filterMethod(value: string) {
  if (!value) {
    selectUsers.value = [...copySelectUsers.value];
  } else {
    // 过滤逻辑省略
  }
}

// 比赛得分
function className(rank: Rank[], index: number) {
  const rankItem = rank[index] || {};
  if (!rankItem.isPractise) return;

  const c = (rankItem.df || 0) / Number(rankItem.score);
  if (c >= 0.7) {
    return "Accepted";
  } else if (c >= 0.3) {
    return "Unaccepted";
  } else {
    return "CompileError";
  }
}

// 补题得分
function spanClassName(rank: Rank[], index: number) {
  const rankItem = rank[index] || {};
  if (!rankItem.isQuestionAnswered) return;

  const c = (rankItem?.matchScore || 0) / Number(rankItem?.matchQScore);
  if (c >= 0.7) {
    return "Accepted";
  } else if (c >= 0.3) {
    return "Unaccepted";
  } else {
    return "CompileError";
  }
}

// 总分
function totalScoreClassName(row: TableData, key: keyof TableData) {
  const totalScore = row.rank?.map((e) => e.score).reduce((a, b) => a + b, 0);
  const rowValue = row[key];
  const numValue = typeof rowValue === "number" ? rowValue : 0;
  const c = numValue / Number(totalScore);
  if (c >= 0.7) {
    return "Accepted";
  } else if (c >= 0.3) {
    return "Unaccepted";
  } else {
    return "CompileError";
  }
}

// 自定义排序方法，按补题得分（df）排序
function sortScoreMethod(key: number) {
  return (a: any, b: any) => {
    // 排序逻辑省略
  };
}

// 弹窗相关
const dialogVisible = ref(false);
const sourceDetail = reactive({
  userName: "",
  name: "",
  score: 0,
  userid: undefined,
  questionid: undefined,
  sourceCode: "",
  info: [],
  result: "",
  sourceLanguage: "",
  createdAt: undefined,
});

const showCode = async (tableRow: any, index: number) => {
  const { userid, name } = tableRow;
  sourceDetail.userName = name || "";
  const item: Rank = tableRow.rank[index];
  sourceDetail.name = item.questionName || "";
  sourceDetail.score = item.df || 0;
  try {
    // 获取代码详情逻辑省略
  } catch (e) {
    console.error(e);
  }
};

function goToSubmission(row: Studycodemodel.Iuserquestion) {
  const url = router.resolve({
    // 路由逻辑省略
  });
  window.open(url.href, "_blank");
}

function goToAssessment(row: Studycodemodel.Iuserquestion) {
  // 跳转评测界面 传入用户和题单号
  if (hasPermission.value) {
    // 评测逻辑省略
  }
}

function time(
  result: Studycodemodel.Iuserquestion["result"],
  info: Studycodemodel.Iuserquestion["info"]
) {
  if (!Array.isArray(info)) return NaN;
  let t = 0;
  for (let v of info) {
    // 计算逻辑省略
  }
  return Math.floor(t) + "ms";
}

function memory(
  result: Studycodemodel.Iuserquestion["result"],
  info: Studycodemodel.Iuserquestioninfo
) {
  if (typeof info.memory === "number") {
    // 处理逻辑省略
  } else if (typeof info.memory === "object") {
    // 处理逻辑省略
  }
  return info.memory || "4kb";
}

function handleClose(done: () => void) {
  dialogVisible.value = false;
  done();
}

function myCopy(str: string) {
  try {
    copy(str);
    ElMessage.success("复制成功");
  } catch (error) {
    console.error(error);
  }
}

// 补题-新开标签页
function toQuestion(tableRow: any, index: number) {
  const item: Rank = tableRow.rank[index];
  const url = router.resolve({
    // 路由逻辑省略
  });
  window.open(url.href, "_blank");
}

// 导出
function excel() {
  const matchQuestionName = matchQuestion.value.map((q) => [q.name, ""]);

  // 一级表头
  const mainHeader = [
    "排名",
    "成员",
    "总分",
    "",
    "",
    ...matchQuestionName.flatMap((e) => e),
  ];

  // 二级表头
  const subHeader = [
    "",
    "",
    "比赛成绩",
    "补题成绩",
    "汇总成绩",
    ...matchQuestion.value.flatMap(() => ["比赛得分", "补题得分"]),
  ];

  const merges: Array<{
    s: { r: number; c: number };
    e: { r: number; c: number };
  }> = [
    // 合并逻辑
    { s: { r: 0, c: 0 }, e: { r: 1, c: 0 } }, // 排名
    { s: { r: 0, c: 1 }, e: { r: 1, c: 1 } }, // 成员
    { s: { r: 0, c: 2 }, e: { r: 0, c: 4 } }, // 总分主标题
  ];

  // 数据部分
  const data = tableData.value.map((row, index) => {
    const { ID, name, xingming, matchTotalScore, totalScore, rank } = row;

    // 动态拼接题目得分，按两列分别生成比赛得分和补题得分
    const questionScores = (rank || []).flatMap((q) => [
      q.matchScore === q.matchQScore && !q.isPractise
        ? q.matchScore
        : q.matchScore, // 比赛得分
      q.isPractise ? q.df : "未补题", // 补题得分
    ]);

    return [
      index + 1,
      xingming ? `${xingming} (${name})` : name,
      row.isAnswered ? matchTotalScore : "-",
      row.isPractise ? totalScore : "未补题",
      row.maxTotalScore,
      ...questionScores,
    ];
  });

  // 合并表头和数据
  const finalData = [mainHeader, subHeader, ...data];
  console.log("finalData=>", finalData);

  // 导出为 Excel
  getExcelMerges(finalData, `${activity.name}比赛成绩表`, merges, 2);
}
</script>

<style scoped>
.rank-box {
  margin: 0;
}

.header {
  display: flex;
  align-items: center;
}

.margin-bottom20 {
  margin-bottom: 20px;
}

.fixed-header .el-table__header-wrapper {
  position: sticky;
  top: 0;
  z-index: 10;
}

.rank-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
}

.refresh {
  border-radius: 5px;
}

.cu:hover {
  cursor: pointer;
}

.match-rank-table-header-qNo,
.match-rank-table-header-qScore {
  position: relative;
}

.match-rank-table-header-qScore {
  color: #7f7f7f;
}

.tips {
  position: absolute;
  left: 0;
}

.score-box {
  padding: 4px 8px;
  border-radius: 4px;
  display: inline-block;
  min-width: 40px;
  text-align: center;
}

.score-box:hover {
  opacity: 0.8;
}

.dialog-content {
  min-height: 400px;
}

.left {
  padding-right: 16px;
  border-right: 1px solid #e0e0e0;
}

.right {
  padding-left: 16px;
}

.source-detail-title {
  margin-bottom: 16px;
  padding-bottom: 8px;
  border-bottom: 1px solid #e0e0e0;
}

.source-code-title,
.test-case-title {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.source-code-title {
  border-bottom: 1px solid #e0e0e0;
  padding-bottom: 8px;
}

/* 测试点 */
.test-case-container {
  margin-bottom: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  overflow: hidden;
}

.test-case-container .test-case-content {
  padding: 8px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.test-case-content .test-case-num {
  font-weight: bold;
}

.test-case-content .test-case-score {
  color: #409eff;
}

.test-case-content .test-case-time {
  color: #909399;
  font-size: 12px;
}
</style>

<style>
#activity-common-layout .match-rank-table-header .cell .caret-wrapper {
  height: auto;
}

#activity-common-layout .match-rank-table-header .cell {
  padding: 0;
  white-space: pre-wrap;
  line-height: normal;
}

#activity-common-layout .score-box.Accepted {
  background-color: #67c23a;
  color: white;
}

#activity-common-layout .score-box.Unaccepted {
  background-color: #e6a23c;
  color: white;
}

#activity-common-layout .score-box.CompileError {
  background-color: #f56c6c;
  color: white;
}

#activity-common-layout .score.Accepted {
  color: #67c23a;
  font-weight: bold;
}

#activity-common-layout .score.Unaccepted {
  color: #e6a23c;
  font-weight: bold;
}

#activity-common-layout .score.CompileError {
  color: #f56c6c;
  font-weight: bold;
}

#activity-common-layout .dialog .el-dialog__body {
  padding: 20px;
}
</style>
